﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using snat.algorithms;
using snat.model;
using snat.util;

namespace algorithms.algorithms.EventRankAlgorithm
{
    /// <summary>
    /// Provides a concrete implementation of the EventRank algorithm, as described in the paper
    /// "EventRank: A Framework for Ranking  TimeVarying Networks" available online at
    /// http://portal.acm.org/citation.cfm?id=1134273 (accessed 01.05.2011)
    /// </summary>
    public class EventRank : IAlgorithm, ITextRenderable
    {
        /*magic numbers for gui*/
        static int textResultsToShow = 7;
        static int textPos = 0;
        static int textPeriod = 15;
        List<DataSeries> serieslist;
        DataChart chart;
        /*end of gui magic*/

        public event RenderTextDelegate RenderText;

        public double F {
            get;
            private set;
        }
        /// <summary>
        /// The EventRank scores produced by the algorithm at the current network state.
        /// A dictionary from int NodeID to double rankvalue
        /// </summary>
        public Dictionary<int, double> Scoring {
            get;
            private set;
        }

        /// <summary>
        /// A list of basenodes ordered from highest to lowest ranking.
        /// </summary>
        public List<BaseNode> Ranking {
            get {
                List<KeyValuePair<int, double>> ordering = Scoring.OrderByDescending(e => e.Value).ToList();
                return ordering.ConvertAll(kvp => NetworkModel.Instance.Nodes[kvp.Key]);
            }
        }

        /// <summary>
        /// Constructor with one optional parameter. Simply initializes the value of F.
        /// </summary>
        /// <param name="_f">the f value to use. Defaults to 0.2.</param>
        public EventRank(double _f = 0.2) {
            F = _f;
        }

        /// <summary>
        /// Initialises the initial ranks to 1 / |C|
        /// </summary>
        public void Init() {
            double nc = (double)NetworkModel.Instance.Nodes.Count;
            Scoring = NetworkModel.Instance.Nodes.Keys.ToDictionary(e => e, e => 1.0 / nc);
        }


        /// <summary>
        /// Called every time a message is sent within the network. Updates EventRank. 
        /// This is the principle logic as described in the paper listed above.
        /// </summary>
        /// <param name="m">The message sent. Ignored in this algorithm</param>
        private void OnEvent(Message m) {
            //Get the participants in the message
            List<int> participants = m.Recipients.ConvertAll(e => e.ID);
            participants.Add(m.Sender.ID);
            participants = participants.Distinct().ToList(); //accounts for copying a message to oneself

            //get the non-participants in the message
            var nonparticipants = from c in Scoring.Keys
                                  where !(participants.Contains(c))
                                  select c;

            //Get Tn, the sum of the scores of non-participants.
            double Tn = (
                from np in nonparticipants
                select Scoring[np]).Sum();

            double alpha = F * Tn;
            double sum_Ri_bar = (
                from p in participants
                select 1.0 - Scoring[p]).Sum();

            foreach (int pp in participants.ToList()) {
                double Ri_last = Scoring[pp];
                double Ri_bar = 1.0 - Ri_last;
                Scoring[pp] = (Ri_last + alpha * (Ri_bar / sum_Ri_bar));
            }

            foreach (int np in nonparticipants.ToList()) {
                Scoring[np] = Scoring[np] * (1.0 - (alpha / Tn));
            }
            if (RenderText != null && textPos++%textPeriod==0) RenderText(this);
        }

        #region IAlgorithm Members
        /// <summary>
        /// Algorithm name.
        /// </summary>
        public string Name {
            get { return "EventRank"; }
        }
        /// <summary>
        /// Connects the algorithm to all relevent events within networkmodel
        /// </summary>
        public void Register() {
            NetworkModel.Instance.TapeLoaded += Init;
            NetworkModel.Instance.MessageProcessed += OnEvent;
        }


        /// <summary>
        /// disconnects the algorithm from the networkmodel
        /// </summary>
        public void DeRegister() {
            NetworkModel.Instance.MessageProcessed -= OnEvent;
            NetworkModel.Instance.TapeLoaded -= Init;
        }


        /// <summary>
        /// Provides textual output to the UI
        /// </summary>
        /// <returns></returns>
        public string GetRenderableText() {
            StringBuilder ret = new StringBuilder();
            ret.Append("\t\tAfter "); ret.Append((textPos-1).ToString()); ret.AppendLine(" messages");
            foreach (KeyValuePair<int, double> kvp in Scoring.OrderByDescending(e => e.Value).Take(textResultsToShow)) {
                ret.Append(NetworkModel.Instance.Nodes[kvp.Key].Name);
                ret.Append(" : ");
                ret.AppendLine(kvp.Value.ToString());
            }
            return ret.ToString();
        }
        #endregion

    }
}
